package org.boot.easyapi.logger.aspect;

import com.alibaba.fastjson.JSON;
import io.swagger.v3.oas.annotations.Operation;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.boot.easyapi.logger.dao.OperationLoggerRepository;
import org.boot.easyapi.logger.entity.OperationLoggerDO;
import org.boot.easyapi.pojo.auto.AuthenticationUserInfo;
import org.boot.easyapi.util.ObjectUtil;
import org.boot.easyapi.util.web.AuthenticationUtil;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Slf4j
@Aspect
@Component
public class OperationLoggerAspect {

    @Value("${easyapi.logger.enabled:false}")
    private boolean enabled;

    @Resource
    private OperationLoggerRepository operationLoggerRepository;

    @Around("execution(* org.boot.easyapi..*Controller.*(..))")
    public Object record(ProceedingJoinPoint pjp) throws Throwable {
        if (!enabled) return pjp.proceed();

        System.out.println("\n");
        log.info("请求日志记录开始---------------------------------------------┐");
        OperationLoggerDO logger = new OperationLoggerDO();
        ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest req = attr.getRequest();
        logger.setUrl(req.getRequestURL().toString());
        logger.setMethod(req.getMethod());
        logger.setIp(req.getRemoteAddr());
        logger.setClassName(pjp.getSignature().getDeclaringTypeName());
        logger.setMethodName(pjp.getSignature().getName());

        Object[] args = pjp.getArgs();

        List<Object> safeArgs = Arrays.stream(args)
                .filter(o -> !(o instanceof HttpServletRequest) &&
                        !(o instanceof HttpServletResponse) &&
                        !(o instanceof MultipartFile) &&           // 单个文件
                        !(o instanceof MultipartFile[]))           // 批量文件
                .collect(Collectors.toList());
        logger.setRequestParams(JSON.toJSONString(safeArgs));

        logger.setCreateTime(LocalDateTime.now());

        //记录操作人
        AuthenticationUserInfo userInfo = AuthenticationUtil.getAuthenticationUserInfo();
        if(userInfo != null){
            logger.setOperationUser(ObjectUtil.isNotEmpty(userInfo.getUsername())
                    ? userInfo.getUsername().toString()
                    : "未定义");
            logger.setOperationUserJson(JSON.toJSONString(userInfo));
        }

        MethodSignature ms = (MethodSignature) pjp.getSignature();
        Method method = ms.getMethod();
        Operation op = method.getAnnotation(Operation.class);
        String summary = op == null ? "" : op.summary();
        logger.setOperationTag(summary);

        log.info("请求参数: {}", logger.getRequestParams());
        log.info("请求路径: {}", logger.getUrl());
        log.info("请求方法: {}", logger.getMethod());
        log.info("请求IP: {}", logger.getIp());
        log.info("请求类: {}", logger.getClassName());
        log.info("请求方法: {}", logger.getMethodName());

        long start = System.currentTimeMillis();
        Object resp = null;
        try {
            resp = pjp.proceed();
            logger.setSuccess("true");
            logger.setResponseResult(JSON.toJSONString(resp));
        } catch (Throwable ex) {
            logger.setResponseResult("ERROR: " + ex.getMessage());
            logger.setSuccess("false");
            log.error("请求日志记录异常---------------------------------------------┘");
            throw ex;
        } finally {
            logger.setExecutionTime(System.currentTimeMillis() - start);
            operationLoggerRepository.save(logger);
        }
        log.info("请求耗时: {}ms", logger.getExecutionTime());
        log.info("请求日志记录完成---------------------------------------------┘");
        System.out.println("\n");

        return resp;
    }
}
